/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hpssim;

import java.util.ArrayList;
import java.util.Iterator;
import javax.print.attribute.standard.JobSheets;
import org.apache.commons.math.*;
import org.apache.commons.math.distribution.PoissonDistributionImpl;
import org.apache.commons.math.random.RandomDataImpl;
/**
 *
 * @author Igor Neri <igor@cimice.net>
 */
public class Simulator {
    

    Simulator(DeviceList hw,int _njobs, int _quantum, double classificationRate, double RealTimeJobsProb, double hjobrate, int avgta) {
        this.tq = _quantum;
        this.njobs = _njobs;
        this.classificationRate = classificationRate;
        this.RealTimeJobsProb = RealTimeJobsProb;
        this.hjobrate = hjobrate;
        this.hw = hw;
        this.avgta = avgta;
    }

    public void init() {
        System.out.println("Start initialization...");
        scheduler = new Scheduler(Scheduler.FIFO);
        RandomDataImpl randomJob = new RandomDataImpl();        
        //randomJob.reSeed(Simulator.tq);
        //SOLO I JOB UTENTE POSSONO ESSERE CPUoGPU
        int rtjobs=0;
        int ujobs = 0;
        int geninterarrivo = 0;
        for (int i = 1; i <= njobs; i++) {
            // Create new job
            /*               ta,rcpu,rgpu,type*/
            geninterarrivo += (int)randomJob.nextExponential(this.avgta);            
            Job job = new Job(geninterarrivo);
            /*Random job type*/
            if (randomJob.nextUniform(0, 1) > RealTimeJobsProb){
                /*user job*/
                job.setjobtype(1);
                /*uniform to selection gpu job according to gpujobrat to implement                
                 *here*/
                job.setrcpu((int)randomJob.nextUniform(Simulator.tq,time_sim/100));
                if (randomJob.nextUniform(0, 1) > hjobrate ){
                    /*CPU JOB */
                    /*GPU time is long*/
                    job.setrgpu((int)(job.getrcpu() * randomJob. nextUniform(1, 300)));
                    
                }
                else{
                    /*GPU*/
                    /*using avg speedup between 2 e 40x*/
                    job.setrgpu((int)(job.getrcpu() / randomJob.nextUniform(2, 40)));                   
                }
                
                               
                if (randomJob.nextUniform(0, 1) > this.classificationRate){
                    /*sbaglio classificazione*/
                    if (job.getrcpu() > job.getrgpu() ){
                        job.setclass(0);
                    }
                    else{
                        job.setclass(1);
                    }
                }
                else{
                    /*azzecco classificazione*/
                    if (job.getrcpu() > job.getrgpu() ){
                        job.setclass(1);
                    }
                    else{
                        job.setclass(0);
                    }
                    
                }
                ujobs++;
            }
            else{
                /*rt job*/
                job.setjobtype(0);
                job.setrcpu((int)randomJob.nextUniform(Simulator.tq/2 ,Simulator.tq*3));
                job.setrgpu(-1);
                job.setclass(0);
                rtjobs++;
            }
            /*Settare priorità di default se RT o user
             *  rt   1..99
             *  user 100 ..140 
             */
            if (job.gettype() == 0 ){
               /* RT */
                job.setpp(3);
                job.setps(90);
                
            }
            else{
                job.setpp(1);
                job.setps(20);
            }
            
            
            job.printJob();          
            Event ev = new Event(job, Event.ENQUEUE, job.ta);
            events.insertEvent(ev);
        }
        System.out.println("RT " + rtjobs +" - User " +ujobs);
        //System.out.println(RealTimeJobsProb);
        hw.infosystem();
        System.out.println("done");
    }

    public void simulate() {
        System.out.println("Start simulation...");
        System.out.println("Current Event - Ta - Next Event Generate - Ta - Device Status - Queue Size | Queue");
        int lastEvTime = 0;
        while (lastEvTime < (time_sim) && (!events.list.isEmpty())) {
            Event ev = nextEvent();
            lastEvTime = ev.time;
            //printProgBar((int) (lastEvTime / time_sim * 100));
        }
        //q.printpriority();
        //jobended.printjob();
        System.out.println("");
        jobended.printJobsStat();
    }

    private Event nextEvent() {
        Event ev = events.pop();       
        Event ev1;
        Job j;        
        switch (ev.type) {
            case Event.ENQUEUE:
                System.out.print("ENQUEUE " + ev.time+ " ");               
                // call scheduler to rearrange start time
                // Insert a Job in Queue
                //scheduler.schedule(events, ev, dev, q);
                scheduler.enqueue(events, ev, hw, q);
                System.out.print("Queue | ");
                q.printjob();
                break;
            case Event.RUN:
                System.out.print("RUN " + ev.time+ " ");                
                scheduler.run(events, ev, hw, q);
                System.out.print("Queue | ");
                q.printjob();
                break;
            case Event.RESCHEDULE: // each RESCHEDULE events has job = null
                // rearrange priority on queued jobs and set next run job
                // call scheduler                
                System.out.print("RESCHEDULE " + ev.time+ " ");    
                scheduler.reschedule(events, ev, hw, q);
                System.out.print("Queue | ");
                q.printjob();
                break;
            case Event.FINALIZE:
                // deallocate device and recall scheduling procedure
                // free device
                System.out.print("FINALIZE " + ev.time+ " ");                                
                scheduler.finalize(events, ev, hw, q, jobended);                
                break;
            case Event.REQUEUE:
                // Add old Job in queue set reschelude attrib for job ++               
                System.out.print("REQUEUE " + ev.time+ " ");
                scheduler.requeue(events, ev, hw, q);
                System.out.print("Queue | ");
                q.printjob();
                break;
            case Event.NULL:
                /*NOT used*/
                break;
        }
        System.out.println();
        System.out.println("----------------------------------------");
        return ev;

    }
    public Queue getJobsEnded(){
        return this.jobended;
    }
    public void setNewQtime(int nqt){
       this.tq = nqt;
    }
    public static void printProgBar(int percent) {
        StringBuilder bar = new StringBuilder("[");

        for (int i = 0; i < 50; i++) {
            if (i < (percent / 2)) {
                bar.append("=");
            } else if (i == (percent / 2)) {
                bar.append(">");
            } else {
                bar.append(" ");
            }
        }

        bar.append("]   ").append(percent).append("%     ");
        System.out.print("\n" + bar.toString());
    }
    private EventList events = new EventList();
    private int njobs;
    public double time_sim = 1000000;
    private double RealTimeJobsProb;
    private double hjobrate;
    private Scheduler scheduler;
    public static int tq; // quantum base time
    private Queue q = new Queue(1);
    public static double classificationRate;
    public Queue jobended = new Queue(0);
    private int avgta;
    private DeviceList hw;
}
